5.02. Django
Django
Что такое Django?
Django — это свободный, высокий, серверный (бэкенд) веб-фреймворк, написанный на языке программирования Python и распространяемый под лицензией BSD. Его ключевой принцип — «разработка, ориентированная на повторное использование и „не повторяй себя“ (DRY)». Django не является микросервисной платформой или узкоспециализированным решением: он представляет собой монолитный, полный стек, предназначенный для быстрого создания веб-приложений со сложной логикой, строгой структурой и высокой степенью интеграции между компонентами. Именно это делает его особенно ценным в образовательных целях, в государственных и корпоративных проектах, где критичны надёжность, документируемость и предсказуемость поведения системы.
Несмотря на то, что в англоязычной литературе Django часто называют фреймворком по шаблону MTV (Model–Template–View), технически он реализует классическую архитектуру MVC (Model–View–Controller), в которой компонент View отвечает за логику представления данных пользователю, а Controller частично реализуется механизмами маршрутизации и обработчиков запросов внутри фреймворка. В контексте Django View играет роль контроллера, а Template — представления. Терминологическое расхождение носит исторический характер и не влияет на архитектурные свойства фреймворка.
История создания и эволюция версий
Django появился в 2003 году в газете The Lawrence Journal-World (штат Канзас, США), где разработчики Эдриан Холловей (Adrian Holovaty) и Саймон Уиллисон (Simon Willison) создали внутренний инструментарий для ускорения производства новостных веб-проектов. Главной задачей было сократить время от идеи до запуска — отсюда и девиз фреймворка: «The web framework for perfectionists with deadlines».
Первая публичная версия (0.90) была выпущена в июле 2005 года. С тех пор развитие фреймворка стало общественным: к 2008 году — 1.0 (знаменательная версия, ввёдшая чёткий принцип обратной совместимости); к 2013 — 1.6 (поддержка транзакций, улучшенная admin-панель); 1.8 (LTS — длительная поддержка); 2.0 (требование Python 3.5+, упрощение URL-механизмов); 3.0 (асинхронные представления, ASGI); 3.2 (LTS); 4.0 (удаление устаревших API); 4.2 (LTS, до апреля 2026 года); и, наконец, 5.0 — выпущенная в декабре 2023 года, с усилением асинхронных возможностей, обновлённым шаблонизатором, расширением поддержки PostgreSQL-специфичных типов и улучшенной системой миграций.
Каждая LTS-версия (Long-Term Support) получает исправления безопасности и критические фиксы в течение трёх лет, что делает их предпочтительными для промышленного развёртывания.
Архитектура Django
Django строится как многоуровневая система:
- Ядро фреймворка — набор базовых модулей, отвечающих за инициализацию, маршрутизацию, обработку исключений, настройки и управление приложениями.
- Приложения (apps) — логические модули проекта, изолированные по функциональности. Например,
users,blog,payments. Каждое приложение — это самостоятельный Python-пакет со своей структурой. - Слой представления (views) — обработчики HTTP-запросов. Могут быть реализованы как функции (
function-based views) или как классы (class-based views, CBV), что позволяет использовать наследование, композицию и полиморфизм. - Шаблонизатор (templates) — система преобразования данных в HTML (или иной текстовый формат). Поддерживает наследование шаблонов, включение, теги, фильтры, автоматическое экранирование.
- Модельный слой (models) — описание структуры данных, отображаемой в реляционную СУБД через ORM. Модели определяют поля и поведение (методы экземпляра, менеджеры, пользовательские QuerySet’ы).
- Система маршрутизации (URL dispatcher) — механизм сопоставления входящих URL с обработчиками (views) посредством регулярных выражений или path-конвертеров.
- Мидлвары (middleware) — цепочка промежуточных обработчиков, перехватывающих запрос до попадания в view и ответ перед отправкой клиенту. Позволяют реализовать аутентификацию, CORS, логирование, кэширование и другие кросс-функциональные задачи.
- Система сигналов (signals) — механизм слабосвязанного взаимодействия между компонентами. Аналог событийной модели, но без встроенной гарантии доставки.
Все эти слои работают в едином контексте, управляемом загрузчиком Django (django.setup()), инициируемом при запуске сервера или выполнении команды через manage.py.
Структура Django-проекта
При создании проекта командой django-admin startproject mysite генерируется следующая древовидная структура:
mysite/
├── manage.py
├── mysite/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ ├── wsgi.py
│ └── ...
└── ...
Разберём каждый элемент:
-
manage.py— точка входа для выполнения административных задач: создание приложений, применение миграций, запуск сервера, тестирование. Это скрипт, который устанавливает переменную окруженияDJANGO_SETTINGS_MODULEи вызываетdjango.core.management.execute_from_command_line. -
__init__.py— пустой файл, обозначающий, что каталогmysite/является Python-пакетом. Без него импорты не работают. -
settings.py— центральный конфигурационный файл. Содержит параметры:DEBUG(режим отладки — ни при каких условиях не включать в продакшене),ALLOWED_HOSTS(список доменов, на которых разрешено развёртывание),INSTALLED_APPS(список активных приложений, включая встроенные:django.contrib.admin,django.contrib.authи т.п.),MIDDLEWARE(цепочка мидлвар),ROOT_URLCONF(путь к главному файлу маршрутов),DATABASES(конфигурация СУБД — по умолчанию SQLite),TEMPLATES,STATIC_URL,STATICFILES_DIRS,MEDIA_URL,SECRET_KEYи многие другие.
-
urls.py— корневой маршрутизатор. Импортируетpath,include, и определяет списокurlpatterns, в котором каждый элемент связывает путь (например,"","admin/","blog/") с обработчиком — либо напрямую с view, либо через делегирование подприложению с помощьюinclude('blog.urls'). -
wsgi.py— точка входа для WSGI-совместимых серверов (Gunicorn, uWSGI). Используется в традиционных синхронных развёртываниях. -
asgi.py— аналог для ASGI (Asynchronous Server Gateway Interface), необходим для обработки веб-сокетов, long polling и асинхронных view. Основан на стандарте ASGI 3.0.
После создания приложения (python manage.py startapp blog) в нём появляется:
-
apps.py— класс конфигурации приложения (BlogConfig), позволяющий задавать метаданные (имя, метку, готовность к запуску черезready()). -
models.py— место определения моделей данных. Каждая модель — это класс, наследуемый отdjango.db.models.Model. Поля объявляются как атрибуты класса. -
views.py— обработчики запросов. В простейшем случае — функция, принимающаяrequestи возвращающаяHttpResponse. В сложных — классы, наследующиеView,TemplateView,ListViewи прочие. -
admin.py— регистрация моделей в интерфейсе администратора. Позволяет автоматически генерировать CRUD-интерфейс без написания HTML. -
tests.py— тесты (рекомендуется выносить в отдельный каталогtests/, но по умолчанию — здесь). -
migrations/— каталог, в которыйmakemigrationsпомещает Python-файлы, описывающие изменения схемы БД. Каждая миграция — это класс, наследуемый отMigration, содержащийoperations(список операций вродеCreateModel,AddField) иdependencies. -
static/иtemplates/— папки для статики (CSS, JS, изображения) и шаблонов соответственно. Django находит их по путям, указанным вSTATICFILES_DIRSиDIRSвнутриTEMPLATES.
Шаблоны организуются по принципу наследования. Например, base.html определяет общую структуру с {% block content %}{% endblock %}, а blog/post_list.html наследует его и переопределяет блок.
Компоненты Django
Django называют «фреймворком с батарейками», поскольку он включает в себя каркас для маршрутизации, представлений, и полный набор инструментов, достаточных для построения полноценного веб-приложения «из коробки». Эти компоненты органично вшиты в архитектуру, имеют унифицированный API и могут быть отключены или заменены, но их наличие обеспечивает согласованность разработки и снижает порог входа.
ORM (Object-Relational Mapper)
Ядро модельного слоя. Позволяет описывать сущности предметной области на языке Python, не касаясь SQL напрямую. Преобразует операции над объектами (BlogPost.objects.filter(published=True)) в оптимизированные SQL-запросы, адаптированные под конкретную СУБД (PostgreSQL, MySQL, SQLite, Oracle). ORM поддерживает транзакции (atomic), агрегации (Count, Sum, Avg), аннотации, подзапросы, сырой SQL при необходимости (extra, raw), и гарантирует защиту от SQL-инъекций за счёт параметризованных запросов.
Шаблонизатор
Система, построенная на принципах наследования, композиции и декларативного вывода. Шаблоны — это текстовые файлы с синтаксисом тегов ({% ... %}) и переменных ({{ ... }}).
{% extends "base.html" %}— наследование.{% block name %}...{% endblock %}— переопределяемые участки.{% include "partial.html" %}— вставка.{% if user.is_authenticated %}...{% endif %}— условная логика (ограниченная — бизнес-логика не должна быть в шаблоне).{{ value|date:"d.m.Y" }}— фильтры (преобразования данных, применяемые в момент рендеринга).
Шаблонизатор автоматически экранирует переменные (<script> → <script>), предотвращая XSS. Экранирование можно отключить ({{ html|safe }}), но только при полной уверенности в источнике.
Встроенная админка (django.contrib.admin)
Не просто интерфейс для CRUD — это полностью настраиваемое приложение, построенное на тех же моделях, формах и представлениях, что и пользовательский код. Админка генерирует формы на основе метаданных моделей, учитывает права доступа (User, Group, Permission), поддерживает поиск, фильтрацию, массовые действия. При этом её можно расширять: переопределять шаблоны, добавлять кастомные action’ы, интегрировать сторонние виджеты (например, редакторы Markdown).
Система аутентификации и авторизации (django.contrib.auth)
Реализует стандартные механизмы:
- Регистрация/вход/выход/сброс пароля.
- Модели
User,Group,Permission. - Декораторы (
@login_required,@permission_required) и миксины (LoginRequiredMixin). - Бэкенды аутентификации (по умолчанию — по логину/паролю, но можно подключить LDAP, OAuth2 через сторонние пакеты).
- Сессии (
django.contrib.sessions), хранящие состояние между запросами (в БД, кэше, файлах).
Система не навязывает единый подход к разграничению доступа: можно использовать проверки на уровне представлений, шаблонов ({% if perms.blog.add_post %}), или даже в ORM (get_queryset() в админке).
REST API и django-rest-framework
Сам Django не включает полноценную поддержку REST — это делает сторонний, но де-факто стандартный пакет Django REST Framework (DRF). Однако ядро предоставляет всё необходимое для его работы: сериализаторы могут опираться на модели, маршрутизация — на URL-конфигурацию, аутентификация — на contrib.auth. DRF добавляет:
- Сериализаторы (
ModelSerializer,HyperlinkedModelSerializer). - Классы представлений (
APIView,GenericAPIView,ViewSet). - Роутеры (
SimpleRouter,DefaultRouter). - Пагинацию, фильтрацию, документацию (Swagger/OpenAPI).
Без DRF можно построить API вручную — через JsonResponse и @csrf_exempt, но это нарушает принцип DRY и не масштабируется.
Система кэширования (django.core.cache)
Интерфейс абстрагирует конкретные бэкенды:
- В памяти (
LocMemCache) — для разработки. - В Redis/Memcached — для продакшена.
- В файловой системе (редко).
Кэширование может применяться:
- На уровне всего сайта (
CacheMiddleware). - На уровне view (
@cache_page). - На уровне шаблона (
{% load cache %}{% cache 600 sidebar %}...{% endcache %}). - На уровне данных (
cache.set,cache.get).
Поддерживает теги зависимости и автоматическую инвалидацию при использовании cacheops или кастомной логики.
Прочие встроенные компоненты
- Формы (
django.forms) — валидация, приведение типов, рендеринг HTML, защита от CSRF. Умеют работать с моделями (ModelForm). - Мидлвары — стандартные:
AuthenticationMiddleware,SessionMiddleware,CsrfViewMiddleware,SecurityMiddleware. Позволяют внедрять логику «поперёк» запроса. - Интернационализация (
i18n) — маркировка строк (gettext,_), переключение языка, форматирование дат/чисел под локаль. - Система сигналов —
pre_save,post_save,m2m_changed. Используется для триггерной логики, но не заменяет прямые вызовы методов. - Тестовый клиент (
django.test.Client) — имитация HTTP-запросов внутри тестов без запуска сервера.
Эти компоненты не изолированы: админка использует формы, формы опираются на валидацию, валидация может задействовать сигналы, сигналы — писать в кэш. Такая композиция обеспечивает целостность системы.
Маршрутизация
Маршрутизация в Django — это иерархическая система сопоставления, где корневой urls.py делегирует подпути приложениям.
Маршрутизатор (URL dispatcher)
Определяется как список urlpatterns, содержащий объекты path() или re_path().
path('blog/<int:year>/', views.year_archive)— использует встроенные конвертеры (int,str,slug,uuid,path).re_path(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive)— для сложных паттернов.
Каждый элемент маршрута может:
- Напрямую указывать на функцию/класс (
views.PostList). - Делегировать поддерево через
include('blog.urls'). - Иметь имя (
name='post_detail'), чтобы генерировать URL в коде или шаблонах ({% url 'post_detail' pk=5 %}).
При запросе GET /blog/2024/ Django:
- Ищет совпадение в
ROOT_URLCONF. - Находит
path('blog/', include('blog.urls')). - Передаёт остаток пути (
2024/) вblog/urls.py. - Там сопоставляет с
path('<int:year>/', views.archive). - Вызывает
views.archive(request, year=2024).
Это — диспетчеризация, а не простое регулярное выражение: порядок маршрутов важен (первое совпадение выигрывает), и система не допускает неоднозначности без явного указания.
Жизненный цикл разработки
Процесс создания приложения в Django строго регламентирован — это гарантия воспроизводимости.
-
Создание проекта
django-admin startproject mysite
cd mysite -
Создание приложения
python manage.py startapp blogЗатем добавляем
'blog'вINSTALLED_APPS. -
Настройка базы данных
Вsettings.py:DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'myuser',
'PASSWORD': 'mypass',
'HOST': 'localhost',
'PORT': '5432',
}
}(Для разработки допустим
sqlite3, но не в продакшене.) -
Определение моделей
Вblog/models.py:from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE) -
Генерация и применение миграций
python manage.py makemigrations
python manage.py migrateКоманда
makemigrationsанализирует изменения в моделях и создаёт Python-файл вblog/migrations/0001_initial.py.migrateприменяет его к БД. -
Создание представлений
Вblog/views.py:from django.shortcuts import render
from .models import Post
def post_list(request):
posts = Post.objects.filter(published=True).order_by('-created_at')
return render(request, 'blog/post_list.html', {'posts': posts}) -
Настройка маршрутов
blog/urls.py:from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
]mysite/urls.py:from django.urls import include, path
urlpatterns = [
path('blog/', include('blog.urls')),
path('admin/', admin.site.urls),
] -
Шаблоны
blog/templates/blog/post_list.html:{% extends "base.html" %}
{% block content %}
<h1>Публикации</h1>
<ul>
{% for post in posts %}
<li>{{ post.title }} ({{ post.created_at|date:"d.m.Y" }})</li>
{% empty %}
<li>Нет опубликованных записей.</li>
{% endfor %}
</ul>
{% endblock %} -
Запуск сервера разработки
python manage.py runserver
Сервер runserver — не для продакшена. Он однопоточный, не обрабатывает статику в DEBUG=False, и не масштабируется. Для развёртывания используют Gunicorn + Nginx или Daphne (для ASGI).
ORM
Типы полей модели
Каждое поле — экземпляр класса Field, определяющий:
- Как данные хранятся в БД.
- Как они валидируются.
- Как они конвертируются в Python-объект.
Ключевые типы:
CharField(max_length=255)— строка фиксированной длины (VARCHAR в SQL). Обязателенmax_length.TextField()— длинный текст (TEXT). Без ограничения длины.EmailField()— валидация по формату email.GenericIPAddressField(protocol='both')— IPv4/IPv6.SlugField()— строка, пригодная для URL (латиница, цифры, дефисы). Часто используется вместе сprepopulated_fieldsв админке.URLField()— валидация URL.UUIDField()— уникальный идентификатор (используется как первичный ключ вместоAutoFieldв распределённых системах).AutoField()/BigAutoField()— автоинкрементный целочисленный PK (по умолчанию дляid).IntegerField(),BigIntegerField(),SmallIntegerField()— целые числа разного диапазона.DecimalField(max_digits=10, decimal_places=2)— точная десятичная дробь (для денег).FloatField()— приблизительное число с плавающей точкой (IEEE 754).DateField(),TimeField(),DateTimeField()— дата, время, дата+время.auto_now=True— обновляется приsave().auto_now_add=True— устанавливается один раз при создании.
DurationField()— разница во времени (timedelta в Python, INTERVAL в PostgreSQL).FileField(upload_to='uploads/'),ImageField()— загрузка файлов.ImageFieldдополнительно проверяет MIME-тип и размеры.BinaryField()— сырые байты (BLOB).JSONField()— хранение структурированных данных (только в PostgreSQL, MySQL 5.7+, SQLite 3.38+).
Поля могут иметь параметры: null, blank, default, choices, help_text, verbose_name.
Методы QuerySet API
ORM возвращает QuerySet — ленивый, цепляемый объект. Выполнение SQL происходит при итерации, срезе, len(), list().
-
filter(**kwargs)— выборка по условиям.Post.objects.filter(published=True, author__name='Иван')
# WHERE published = TRUE AND author.name = 'Иван' -
get(**kwargs)— получение одного объекта. ВыбрасываетDoesNotExistилиMultipleObjectsReturned.post = Post.objects.get(slug='hello-world') -
create(**kwargs)— создание и сохранение за один вызов.Post.objects.create(title='Новая запись', content='...', author=author) -
update(**kwargs)— массовое обновление (без вызоваsave(), без сигналовpre_save).Post.objects.filter(published=False).update(published=True) -
delete()— массовое удаление.Post.objects.filter(created_at__lt='2020-01-01').delete() -
order_by('field'),distinct(),values(),values_list(),annotate(),aggregate()— для сортировки, дедупликации, выборки словарей/кортежей, расчёта агрегатов.
ORM поддерживает связки:
ForeignKey→author__name(двойное подчёркивание для перехода через связь).ManyToManyField→post__tags__name.select_related()— JOIN дляForeignKey(оптимизация N+1).prefetch_related()— отдельный запрос дляManyToManyи обратных связей.
Миграции
Миграции — это код, управляющий структурой БД. Они решают две задачи:
- История изменений (кто, когда, что изменил в схеме).
- Воспроизводимость (новый разработчик или сервер могут достичь актуального состояния БД одной командой).
Процесс:
- Меняется
models.py. makemigrationsсравнивает текущее состояние моделей с последней миграцией и генерирует Python-файл.migrateприменяет миграции в порядке зависимостей.
Миграции — необратимы по умолчанию. --fake и --fake-initial используются только в чрезвычайных ситуациях. Для отката пишут reverse_code вручную или используют migrate <app> <previous_migration>.
Сложные сценарии:
- Данные в миграции — через
RunPython.def populate_authors(apps, schema_editor):
Author = apps.get_model('blog', 'Author')
Author.objects.create(name='Админ', email='admin@example.com') - Разделение миграций — когда одна модель влияет на другую, но приложения независимы.
- Совместимость — миграции должны быть идемпотентными и не ломать работающие приложения при развёртывании «на лету».
Админка Django
Как получить доступ
- Создать суперпользователя:
python manage.py createsuperuser - Запустить сервер:
python manage.py runserver. - Перейти по
/admin/, войти.
Как это работает «под капотом»
Админка — это Django-приложение, зарегистрированное как django.contrib.admin.
admin.site— глобальный инстансAdminSite.admin.site.register(Model, ModelAdmin)— связывает модель с её конфигурацией.ModelAdmin— класс, определяющий:- Какие поля отображать (
list_display,list_filter). - Как искать (
search_fields). - Как редактировать (
fields,fieldsets,readonly_fields). - Как валидировать (
clean()). - Как обрабатывать массовые действия (
actions).
- Какие поля отображать (
Пример настройки:
# blog/admin.py
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'published', 'created_at')
list_filter = ('published', 'created_at')
search_fields = ('title', 'content')
date_hierarchy = 'created_at'
prepopulated_fields = {'slug': ('title',)}
actions = ['make_published']
def make_published(self, request, queryset):
updated = queryset.update(published=True)
self.message_user(request, f'{updated} записей опубликовано.')
make_published.short_description = "Опубликовать выбранные записи"
Админка использует те же шаблоны, что и пользовательские приложения (admin/base_site.html можно переопределить), те же формы (ModelForm), и те же сигналы. Это позволяет, например:
- Добавить в админку график статистики через кастомный view и шаблон.
- Интегрировать внешний API при сохранении объекта.
- Ограничить видимость записей по правам пользователя (
get_queryset()).
Важно: админка — не интерфейс для конечного пользователя. Она предназначена для управления контентом и конфигурацией. Для публичных интерфейсов строятся отдельные представления.
Асинхронность в Django
Django изначально создавался как синхронный фреймворк. Поддержка асинхронности появилась постепенно, начиная с версии 3.0, и реализована как надстройка над существующей архитектурой. Это принципиально: асинхронность в Django — инструмент для конкретных задач.
Что стало возможным
-
Асинхронные представления (async views)
async def async_view(request):
data = await fetch_external_api() # I/O-bound операция
return JsonResponse({'data': data})Такие view обрабатываются ASGI-сервером (Daphne, Uvicorn) без блокировки потока на время ожидания. Однако:
- ORM по-прежнему синхронный. Вызов
await Post.objects.afilter(...)возможен только в асинхронном контексте и требуетasyncpg(PostgreSQL) или совместимого бэкенда. - Синхронный код внутри async-view блокирует весь цикл событий — его следует оборачивать в
sync_to_async.
- ORM по-прежнему синхронный. Вызов
-
ASGI-совместимость
Файлasgi.pyпозволяет развёртывать приложение на серверах, поддерживающих асинхронные протоколы:- HTTP/1.1 и HTTP/2 — обычные запросы.
- Веб-сокеты — через
channels(официальный проект Django).
-
channels— расширение для событийной модели
Не входит в ядро, но рекомендован разработчиками Django. Позволяет:- Обрабатывать веб-сокеты (
AsyncWebsocketConsumer). - Организовывать фоновые задачи через
channel layers(Redis, In-Memory). - Строить чаты, уведомления в реальном времени.
Важно:channelsне заменяет Celery — он для короткоживущих, связанных с соединением событий. Долгие задачи (генерация отчётов, обработка видео) — в Celery или RQ.
- Обрабатывать веб-сокеты (
Ограничения и компромиссы
- ORM остаётся частично синхронным. Полностью асинхронный доступ к данным (
Post.objects.acreate(...)) доступен, но требует явного использования асинхронных методов и поддержки СУБД. - Мидлвары должны быть помечены как sync/async. Смешивание вызывает
SynchronousOnlyOperation. - Производительность не растёт автоматически. Асинхронность эффективна при высокой доле I/O (внешние API, медленные БД), но ухудшает performance при CPU-bound операциях.
- Сложность отладки возрастает. Стек вызовов раздваивается: sync/async boundary требует явного преобразования (
sync_to_async,async_to_sync).
Таким образом, асинхронность в Django — целенаправленный выбор для решения конкретных проблем масштабируемости. Для большинства CRUD-приложений, особенно в госсекторе или корпоративных системах, синхронный стек остаётся предпочтительным: он проще, стабильнее и лучше документирован.
Безопасность
Django включает широкий набор защит по умолчанию, но никакой фреймворк не делает приложение безопасным автоматически.
Защита по умолчанию
- CSRF-токены — включены через
CsrfViewMiddleware. Формы в шаблонах должны содержать{% csrf_token %}. REST API (особенно stateless) требуют отключения или замены на другие методы (например, JWT). - XSS-защита — автоматическое экранирование в шаблонах. Риск возникает только при использовании
|safe,mark_safe(), или ручном формировании HTML. - SQL-инъекции — исключены при использовании ORM и параметризованных запросов. Риск — при
extra(),raw()без экранирования. - Clickjacking — блокируется заголовком
X-Frame-Options: DENY(настраивается черезXFrameOptionsMiddleware). - Безопасные заголовки —
SecurityMiddlewareдобавляет:HTTP Strict Transport Security (HSTS)Content Security Policy (CSP)— требует ручной настройкиReferrer-Policy,X-Content-Type-Options,X-XSS-Protection(устарел, но иногда нужен для старых браузеров).
Что требует явной настройки
- Content Security Policy (CSP) — не включён по умолчанию. Для активации нужен
django-cspили ручное управление заголовками. Без CSP даже экранирование не спасает от атак черезscript-src 'unsafe-inline'. - Rate limiting — отсутствует в ядре. Реализуется через Nginx, Redis +
django-ratelimit, или в мидлваре. - Двухфакторная аутентификация — через сторонние пакеты (
django-otp,django-two-factor-auth). - Логирование безопасности —
SECURE_CONTENT_TYPE_NOSNIFF,SECURE_BROWSER_XSS_FILTERустарели; современный подход — CSP +report-uri.
Критические ошибки разработчиков
- Отключение
DEBUG = Trueв продакшене (раскрывает стектрейсы, настройки, SQL). - Использование
SECRET_KEYв репозитории. - Хранение паролей в открытом виде (ORM не шифрует — только хэширует через PBKDF2/HMAC-SHA256).
- Доверие к
request.META['HTTP_X_FORWARDED_FOR']без валидации (подмена IP). - Неправильная настройка CORS — особенно при использовании DRF.
Безопасность в Django — оборонительная стратегия: фреймворк ставит барьеры по умолчанию, но окончательная ответственность лежит на разработчике.
Тестирование
Django предоставляет мощную тестовую инфраструктуру на базе unittest, расширенную собственными утилитами.
Уровни тестирования
-
Модульные тесты (
TestCase)- Тестируют модели, формы, вспомогательные функции.
- Используют in-memory SQLite (быстро, изолированно).
- Пример:
from django.test import TestCase
from blog.models import Post
class PostModelTest(TestCase):
def test_published_posts(self):
Post.objects.create(title='Черновик', published=False)
Post.objects.create(title='Публикация', published=True)
self.assertEqual(Post.objects.published().count(), 1)
-
Интеграционные тесты (
TransactionTestCase)- Проверяют взаимодействие компонентов: view + модель + шаблон.
- Сохраняют транзакции между тестами (медленнее).
-
Тесты клиентского уровня (
Client,RequestFactory)Client— имитация HTTP-запросов:response = self.client.get('/blog/')
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Публикации')RequestFactory— создаёт объектHttpRequestбез запуска middleware (для тестирования view в изоляции).
-
Тесты шаблонов
- Проверка контекста, использования блоков, фильтров.
response.templates,response.context.
Особенности
- База данных создаётся заново для каждого
TestCase. --keepdbускоряет повторные запуски.pytest-django— альтернатива стандартному runner’у, с поддержкой fixture, параметризации, асинхронных тестов.- Тесты админки:
admin.site.urlsдоступен, можно проверять рендеринг форм, права. - Покрытие:
coverage run manage.py test && coverage report.
Тестирование в Django — не опционально. Это часть жизненного цикла: миграции, админка, формы — всё ломается при несогласованности изменений. Регулярный запуск тестов — условие поддерживаемости.
Развёртывание
Этапы развёртывания
-
Подготовка окружения
DEBUG = FalseALLOWED_HOSTS = ['example.com', 'www.example.com']SECRET_KEY— из переменной окружения (os.getenv('SECRET_KEY')).- Настройка логирования (
LOGGINGвsettings.py).
-
Статические файлы
python manage.py collectstaticСобирает все
static/вSTATIC_ROOT. Обслуживаются веб-сервером (Nginx):location /static/ {
alias /path/to/staticfiles/;
} -
Медиафайлы
Аналогично, но с учётом безопасности:- Ограничение MIME-типов.
- Хранение вне корня проекта.
- Использование signed URLs (через
django-storages+ S3).
-
Сервер приложения
- WSGI: Gunicorn (предпочтительно) + Nginx.
gunicorn mysite.wsgi:application --bind 0.0.0.0:8000 --workers 3 - ASGI: Daphne/Uvicorn — только при использовании веб-сокетов или async views.
- WSGI: Gunicorn (предпочтительно) + Nginx.
-
База данных
- PostgreSQL — рекомендуемый выбор (поддержка
JSONField,ARRAY,HSTORE, надёжность). - Резервное копирование (
pg_dump), мониторинг, connection pooling (pgBouncer).
- PostgreSQL — рекомендуемый выбор (поддержка
-
Мониторинг и логирование
- Sentry — для отслеживания исключений.
- Prometheus + Grafana — метрики (запросы/сек, время ответа).
- ELK-stack — агрегация логов.
Контейнеризация
Docker не обязателен, но упрощает воспроизводимость:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
RUN python manage.py collectstatic --noinput
CMD ["gunicorn", "mysite.wsgi:application", "--bind", "0.0.0.0:8000"]
Важно: не запускать миграции в Dockerfile — только в entrypoint.sh при старте контейнера.
Типичные антипаттерны и как их избежать
-
«Гигантский
views.py»
— Признак: тысячи строк, смесь логики, шаблонов, API.
— Решение:- Вынос бизнес-логики в
services.py. - Использовать классы (
ListView,DetailView). - Разделять на модули:
views/→list.py,detail.py,api.py.
- Вынос бизнес-логики в
-
N+1 запросов
— Признак:post.author.nameв цикле безselect_related.
— Решение:select_related()дляForeignKey.prefetch_related()дляManyToMany.django-debug-toolbarдля выявления.
-
Логика в шаблонах
— Признак:{% if user.is_staff or user.is_superuser or post.author == user %}.
— Решение:- Вынос в метод модели (
post.can_edit(user)). - Использование
TemplateViewс предварительно вычисленным контекстом.
- Вынос в метод модели (
-
Жёсткая привязка к админке
— Признак: «Пользователи редактируют контент только через/admin/».
— Решение:- Построение пользовательских форм и интерфейсов.
- Админка — только для технических администраторов.
-
Игнорирование миграций
— Признак: «Просто написал SQL вручную в pgAdmin».
— Решение:- Всегда использовать
makemigrations. - Проверка миграций в CI (
python manage.py makemigrations --check --dry-run).
- Всегда использовать
Сравнение Django с Flask, FastAPI и Pyramid
Выбор фреймворка — инженерное решение, основанное на требованиях к проекту, а не на хайпе. Django не «лучше» или «хуже» — он другой.
| Критерий | Django | Flask | FastAPI | Pyramid |
|---|---|---|---|---|
| Тип | «Батарейки в комплекте», монолитный | Микрофреймворк, минимализм | Современный, API-first | Гибкий, настраиваемый |
| Архитектура | Жёсткая структура (apps, models, views) | Свободная (любая организация кода) | Слабо структурирован (но рекомендуется routers, schemas) | Явная декларация компонентов |
| ORM | Встроенный (Django ORM) | Нет (обычно SQLAlchemy) | Нет (обычно SQLAlchemy или Tortoise) | Нет (обычно SQLAlchemy) |
| Админка | Да, с полной кастомизацией | Нет (Flask-Admin — отдельный пакет) | Нет | Нет |
| Валидация | В формах и моделях | Ручная или через Marshmallow | Автоматическая (Pydantic) | Через Colander или Marshmallow |
| Асинхронность | Частичная (с версии 3.0) | Через Quart или Flask + asyncio | Полная (на базе Starlette) | Частичная (через pyramid_asyncio) |
| Документация API | Требует DRF + drf-yasg/Swagger | Требует сторонних инструментов | Автоматическая (OpenAPI/Swagger UI) | Требует ручной настройки |
| Скорость разработки | Высокая (MVP за часы) | Средняя (нужно собирать стек) | Высокая для API | Средняя (требует проектирования) |
| Скорость выполнения | Умеренная (накладные расходы каркаса) | Высокая (минимум overhead) | Очень высокая (асинхронность + Pydantic) | Умеренная |
| Типичное применение | Корпоративные системы, CMS, порталы | Микросервисы, прототипы, скрипты | API, микросервисы, ML-инференс | Enterprise-приложения, legacy-интеграция |
Когда выбирать Django?
- Требуется единая точка управления (админка + пользовательский интерфейс + API).
- Проект ориентирован на данные и бизнес-логику, а не на высоконагруженные API.
- Важна предсказуемость и сопровождаемость (госзаказ, финансовые системы).
- Команда включает начинающих разработчиков — структура ускоряет онбординг.
Когда НЕ выбирать Django?
- Чистый REST/gRPC-сервис без веб-интерфейса.
- Высокая нагрузка на чтение (десятки тысяч RPS) — тогда FastAPI + кэш.
- Интеграция с legacy-системами через нестандартные протоколы (тогда Flask + адаптеры).
- Требуется полная свобода выбора ORM (например, переход с SQLAlchemy на Tortoise).
Django — консервативный, проверенный временем выбор для систем, где важнее надёжность и скорость старта, чем предельная производительность.
Расширение через сигналы и middleware
Django позволяет внедрять логику «поперёк» архитектуры без изменения ядра. Это критично для масштабирования и поддержки.
Сигналы: слабосвязанное взаимодействие
Сигналы — аналог событийной модели. Основные встроенные сигналы:
pre_save/post_save— перед/после сохранения объекта.pre_delete/post_delete— перед/после удаления.m2m_changed— при изменении ManyToMany-связи.request_started/request_finished— начало/окончание обработки запроса.
Пример: инвалидация кэша при обновлении поста:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.cache import cache
from .models import Post
@receiver(post_save, sender=Post)
def invalidate_post_cache(sender, instance, **kwargs):
cache.delete(f'post_detail_{instance.pk}')
cache.delete('post_list')
Ограничения сигналов:
- Не гарантируют порядок выполнения.
- Не работают при массовом обновлении (
QuerySet.update()). - Сложно тестировать и отлаживать (неявные зависимости).
- Не заменяют прямые вызовы методов — использовать только там, где нельзя изменить вызывающий код (например, в сторонних приложениях).
Middleware
Мидлвар — класс с методами __call__ или process_request/process_response. Выполняется в порядке, указанном в MIDDLEWARE.
Пример: логирование времени обработки:
import time
from django.utils.deprecation import MiddlewareMixin
class TimingMiddleware(MiddlewareMixin):
def process_request(self, request):
request.start_time = time.time()
def process_response(self, request, response):
duration = time.time() - request.start_time
response['X-Response-Time'] = f'{duration:.3f}s'
return response
Встроенные мидлвары, которые стоит понимать:
SecurityMiddleware— безопасные заголовки.CsrfViewMiddleware— защита от CSRF.SessionMiddleware— загрузка сессии из хранилища.AuthenticationMiddleware— привязкаrequest.user.MessageMiddleware— одноразовые сообщения («Запись сохранена»).
При написании кастомного middleware:
- Избегать тяжёлых операций в
process_request(замедляет все запросы). - Обрабатывать исключения в
process_exception. - Для асинхронных view — наследоваться от
AsyncMiddlewareMixin.
Сигналы и middleware — инструменты ответственности: их чрезмерное использование превращает проект в «лазанью» неявных зависимостей.
Интернационализация (i18n) и локализация (l10n)
Django предоставляет полную поддержку многоязычности — от маркировки строк до форматирования дат под локаль.
Этапы внедрения
-
Маркировка строк
В коде:from django.utils.translation import gettext as _
title = _('Welcome to our site')В шаблонах:
{% load i18n %}
<h1>{% trans "Welcome to our site" %}</h1> -
Генерация
.po-файловdjango-admin makemessages -l ruСоздаёт
locale/ru/LC_MESSAGES/django.po— файл перевода. -
Компиляция в
.modjango-admin compilemessages -
Переключение языка
- Через URL (
/ru/blog/,/en/blog/сi18n_patterns). - Через заголовок
Accept-Language. - Через куки/сессию (
set_languageredirect view).
- Через URL (
Особенности
- Форматирование дат/чисел — зависит от
USE_L10N = TrueиLANGUAGE_CODE.
Например,{{ date|date:"SHORT_DATE_FORMAT" }}→05.12.2025(ru) vs12/05/2025(en). - Множественные формы — через
ngettext:ngettext('%(count)d post', '%(count)d posts', count) % {'count': count} - Перевод моделей — требует сторонних пакетов (
django-modeltranslation,django-parler).
В ядре — только перевод строк интерфейса, не данных.
Интернационализация в Django — зрелая, промышленная система, но требует дисциплины: каждая строка, видимая пользователю, должна быть помечена. Пропуск одной строки ломает весь перевод.
Работа с legacy-кодом
Django часто используется для модернизации старых систем. Ключевые стратегии:
1. Постепенная замена (Strangler Fig Pattern)
- Новый функционал пишется на Django.
- Старая система остаётся, но новые URL перехватываются Django.
- Пример:
- Старый
/old-crm/— обслуживается PHP. - Новый
/crm/— на Django. - Постепенно
/old-crm/*перенаправляются или реверс-проксируются.
- Старый
2. Обёртка вокруг legacy-логики
- Django выступает как «тонкий» фасад:
def legacy_report_view(request):
# Вызов старого скрипта через subprocess или HTTP
result = requests.post('http://legacy/report', json=request.POST)
return JsonResponse(result.json())
3. Совместное использование базы данных
- Django ORM работает с существующей схемой:
class LegacyUser(models.Model):
login = models.CharField(max_length=50)
# ... поля без primary_key=True, если PK не `id`
class Meta:
db_table = 'users_old'
managed = False # Django не создаёт/не меняет таблицу managed = False— критично: миграции не трогают эту таблицу.
4. Интеграция через API
- Legacy-система предоставляет REST/SOAP.
- Django использует
requests,zeep(для SOAP), кэширование, retry-логику. - Для SOAP —
django-soapили ручная обработка черезxml.etree.
5. Тестирование legacy-интеграций
- Mock внешних вызовов (
unittest.mock.patch). - Запись/воспроизведение запросов (
vcr.py). - Проверка идемпотентности (повторный вызов не ломает данные).
Legacy-интеграция — организационная задача: необходимо документировать границы ответственности, точки отказа и процедуры отката.